package com.fc.mall.portal.connector;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fc.mall.exception.ServiceException;
import com.fc.mall.mapper.SysPropertyConfigMapper;
import com.fc.mall.model.SysPropertyConfig;
import com.fc.mall.model.SysPropertyConfigExample;
import com.fc.mall.portal.dto.DecodePhoneParam;
import com.fc.mall.portal.dto.WxLoginRespDto;
import com.fc.mall.portal.service.RedisService;
import com.fc.mall.portal.wechat.dto.WxsmallTemplate;
import com.fc.mall.util.Constants;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.spec.AlgorithmParameterSpec;
import java.util.List;

@Service
public class WeChatConnector {

    private static final Logger logger = LoggerFactory.getLogger(WeChatConnector.class);

    @Value("${wechat.host}")
    private String wxHost;

    private String wxAppId;

    private String wxSecret;

    @Autowired
    private SysPropertyConfigMapper sysPropertyConfigMapper;

    @Autowired
    private RedisService redisService;

    /**
     * 登录凭证校验。通过 wx.login() 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
     *
     * @param jsCode
     * @return
     */
    public WxLoginRespDto wxLogin(String jsCode) {


        initAPPId();
        String loginUrl = new StringBuilder()
                .append(wxHost)
                .append("/sns/jscode2session")
                .append("?appid=" + wxAppId)
                .append("&secret=" + wxSecret)
                .append("&js_code=" + jsCode)
                .append("&grant_type=authorization_code")
                .toString();

        String response;
        try {
            response = Request.Get(loginUrl)
                    .addHeader("Content-Type", "application/json")
                    .execute()
                    .returnContent()
                    .asString(StandardCharsets.UTF_8);

        } catch (Exception e) {
            // 请求网络错误
            throw new ServiceException("请求数据，网络错误!");
        }

        if (response != null) {
            JSONObject respJson = JSON.parseObject(response);
            handleError(respJson);

            WxLoginRespDto loginResp = new WxLoginRespDto();
            loginResp.setOpenid(respJson.getString("openid"));
            loginResp.setSessionKey(respJson.getString("session_key"));
            loginResp.setUnionid(respJson.getString("unionid"));
            return loginResp;
        }
        return null;
    }

    /**
     * 获取小程序全局唯一后台接口调用凭据
     * @return
     */
    public String getAccessToken()
    {
        String accessToken = redisService.get("access_token");
        if(!StringUtils.isEmpty(accessToken))
        {
            //return  accessToken;
        }
        initAPPId();
        String loginUrl = new StringBuilder()
                .append(wxHost)
                .append("/cgi-bin/token")
                .append("?appid=" + wxAppId)
                .append("&secret=" + wxSecret)
                .append("&grant_type=client_credential")
                .toString();

        String response;
        try {
            response = Request.Get(loginUrl)
                    .addHeader("Content-Type", "application/json")
                    .execute()
                    .returnContent()
                    .asString(StandardCharsets.UTF_8);

        } catch (Exception e) {
            // 请求网络错误
            throw new ServiceException("请求数据，网络错误!");
        }

        if (response != null) {
            JSONObject respJson = JSON.parseObject(response);
            handleError(respJson);
            accessToken = respJson.getString("access_token");
            redisService.set("access_token", accessToken);
            redisService.expire("access_token", 60*60);
            return accessToken;
        }
        return null;
    }

    public byte[] generateQrCode(String accessToken, String scene, String page)
    {
        String getwxacodeunlimitUrl = new StringBuilder()
                .append(wxHost)
                .append("/wxa/getwxacodeunlimit")
                .append("?access_token=")
                .append(accessToken)
                .toString();
        HttpResponse response;
        JSONObject json = new JSONObject();
        json.put("scene", scene);
        json.put("page", page);
        try {
            response = Request.Post(getwxacodeunlimitUrl)
                    .addHeader("Content-Type", "application/json")
                    .bodyString(json.toJSONString(), ContentType.APPLICATION_JSON)
                    .execute()
                    .returnResponse();
        }
        catch (Exception e) {
            // 请求网络错误
            throw new ServiceException("请求数据，网络错误!");
        }

        if (response != null) {
            try {
                byte[] bResult = EntityUtils.toByteArray(response.getEntity());
                return bResult;
            } catch (IOException e) {
                return null;
            }
        }

        return null;
    }

    public void sendMsg(WxsmallTemplate template)
    {
        String accessToken = getAccessToken();
        String sendMsgUrl = new StringBuilder(wxHost).append("/cgi-bin/message/wxopen/template/uniform_send")
                .append("?access_token=").append(accessToken).toString();
        String json = JSONObject.toJSONString(template);
        String response;
        try {
            response = Request.Post(sendMsgUrl)
                    .addHeader("Content-Type", "application/json")
                    .bodyString(json, ContentType.APPLICATION_JSON)
                    .execute()
                    .returnContent()
                    .asString(StandardCharsets.UTF_8);
            logger.debug(response);
        }
        catch (Exception e)
        {
            throw new ServiceException("请求数据，网络错误!");
        }

    }

    public String decodeWxAPPPhone(DecodePhoneParam phoneParam) throws Exception
    {
        WxLoginRespDto wxLoginResp = wxLogin(phoneParam.getCode());
        String sessionkey = wxLoginResp.getSessionKey();
        byte[] encrypData = Base64Utils.decodeFromString(phoneParam.getEncrypted());
        byte[] ivData = Base64Utils.decodeFromString(phoneParam.getIv());
        byte[] sessionKey = Base64Utils.decodeFromString(sessionkey);
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(sessionKey, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

        String resultString = new String(cipher.doFinal(encrypData), "UTF-8");
        JSONObject object = JSONObject.parseObject(resultString);
        // 拿到手机号码
        String phone = object.getString("phoneNumber");
        return  phone;
    }
    private void handleError(JSONObject respJson) {
        Integer errcode = respJson.getInteger("errcode");
        // status = 0 --- 正确
        // 其他错误
        if (errcode != null && errcode > 0) {
            String errmsg = respJson.getString("errmsg");
            throw new ServiceException(errmsg);
        }
    }

    private void  initAPPId()
    {
        SysPropertyConfigExample example = new SysPropertyConfigExample();
        example.createCriteria().andCategoryEqualTo(Constants.SysPropertyConfigCategory.WECHAT_SETTING.getValue());

        List<SysPropertyConfig> sysPropertyConfigList = sysPropertyConfigMapper.selectByExample(example);

        if (null == sysPropertyConfigList || sysPropertyConfigList.isEmpty())
        {
            throw new ServiceException("微信配置有误，请联系管理员设置");
        }

        sysPropertyConfigList.forEach( config -> {
            if(config.getCode().equalsIgnoreCase(Constants.SysPropertyConfigName.APPID.getName()))
            {
                this.wxAppId = config.getValue();
            }
            if(config.getCode().equalsIgnoreCase(Constants.SysPropertyConfigName.APPSECRECT.getName()))
            {
                this.wxSecret = config.getValue();
            }
        });
        if(StringUtils.isEmpty(this.wxAppId) || StringUtils.isEmpty(this.wxSecret ))
        {
            throw new ServiceException("微信配置有误，请联系管理员设置");
        }
    }
}
